/* floppydr - floppy "doctor" program: low-low-level access to FDC */ #include #include #include #include #include #define UBYTE unsigned char #define flop_cmd(dr, motflag, moton, c, r, n, b) \ { drive_sel(dr, motflag, moton); \ fdc_cmd(c, r, n, b); \ results(c[0], r);} #define out_dor(cmd) outp(0x3F2, cmd) #define in_stat() (inp(0x03f4)) #define out_dat(d) outp(0x3f5,d) #define in_dat() (inp(0x3f5)) /* notes for Turbo-C: use outportb() and inportb() */ UBYTE buffer[1024]; char get_menu_choice(); UBYTE peekbyte(unsigned, unsigned); long start_tmo(void); #define READ_DATA 6 #define READ_DELETED_DATA 0xC #define WRITE_DATA 5 #define WRITE_DELETED_DATA 9 #define READ_TRACK 2 #define READ_ID 0xA #define FORMAT_TRACK 0xD #define RECALIBRATE 7 #define SENSE_INTR_STATUS 8 #define SPECIFY 3 #define SENSE_DRIVE_STATUS 4 #define SEEK 0xF typedef struct { unsigned int ncmd; unsigned int nres; char ctype; /* N, I, R, or W, for "no DMA", "Interrupt", "Read", "Write") */ } CMD_INFO; CMD_INFO cmdt[16] = { {0, 0, ' '}, /* 0x00 */ {0, 0, ' '}, /* 0x01 */ {9, 7, 'R'}, /* 0x02 read track */ {3, 0, 'N'}, /* 0x03 specify */ {2, 1, 'N'}, /* 0x04 sense drive status */ {9, 7, 'W'}, /* 0x05 write data */ {9, 7, 'R'}, /* 0x06 read data */ {2, 0, 'I'}, /* 0x07 recalibrate */ {1, 2, 'N'}, /* 0x08 sense interrupt status */ {9, 7, 'W'}, /* 0x09 write deleted data */ {2, 7, 'N'}, /* 0x0a read id */ {0, 0, ' '}, /* 0x0b */ {9, 7, 'R'}, /* 0x0c read deleted data */ {6, 7, 'W'}, /* 0x0d format track */ {0, 0, ' '}, /* 0x0e */ {3, 0, 'I'}, /* 0x0f seek */ }; #define NDPARM 12 struct DPARM { UBYTE spec1; /* 0 specify byte 1 */ UBYTE spec2; /* 1 specify byte 2 */ UBYTE mot_off_wt; /* 2 meaningless */ UBYTE nrec; /* 3 number of bytes code */ UBYTE eot; /* 4 end-of-track */ UBYTE gpl; /* 5 gap length */ UBYTE dtl; /* 6 data length (if N = 0) */ UBYTE fgpl; /* 7 gpl for format */ UBYTE fd; /* 8 fill byte for format */ UBYTE hdsttl; /* 9 head settle time (milliseconds) */ UBYTE mot_start; /* 10 motor start time (1/8 seconds) */ UBYTE mt_mfm_sk; /* 11 bits: 0x80 = MT, 0x40 = MFM, 0x20 = SK */ } dparm = {0xcf, 0x02, 0x25, 0x02, 0x12, 0x2A, 0xFF, 0x50, 0xF6, 0x01, 0x04, 0xE0}; static char *dparmmsg[] = { "0 - specify byte 1 (bits 0-3 = HUT, 4-7 = SRT) ", "1 - specify byte 2 (bit 1-7 = HLT, 0 = 'no dma')", "2 - meaningless (was motor start time)", "3 - N: number of bytes code (2=512)", "4 - EOT: end-of-track", "5 - GPL: gap length", "6 - DTL:data length (if N = 0)", "7 - gpl for format", "8 - fill byte for format", "9 - head settle time (milliseconds) (not used)", "10 - motor start time (1/8 seconds) (not used)", "11 - bits: 0x80 = MT, 0x40 = MFM, 0x20 = SK"}; int main() { UBYTE unit = 0; UBYTE track = 0; UBYTE head = 0; UBYTE sector = 1; UBYTE nsector = 9; UBYTE cd[9], rs[7], c, hdsds; int nbyte, i, j; printf("\n\nfloppy doctor program\n\n"); out_dor(0); /* reset card */ out_dor(0xC); /* select drive 0, allow ints & DMA */ while (1) { c = get_menu_choice(); switch (c) { case '0': printf(" Enter Drive number:"); getnum("%d", &unit); cd[0] = SENSE_DRIVE_STATUS; cd[1] = (hdsds = unit + (head << 2)); flop_cmd(unit, 0, 0, cd, rs, 0, buffer); break; case '1': cd[0] = SPECIFY; cd[1] = dparm.spec1; cd[2] = dparm.spec2; flop_cmd(unit, 0, 0, cd, rs, 0, buffer); break; case '2': cd[0] = RECALIBRATE; cd[1] = unit; flop_cmd(unit, 1, 200, cd, rs, 0, buffer); break; case '3': printf(" Enter cylinder (track) number: "); getnum("%d", &track); cd[0] = SEEK; cd[1] = hdsds; cd[2] = track; drive_sel(); flop_cmd(unit, 1, 200, cd, rs, 0, buffer); break; case '4': printf(" Enter Head number:"); getnum("%d", &head); hdsds = (hdsds & 0x3) | head; break; case '5': cd[0] = READ_DATA + dparm.mt_mfm_sk; goto read_write; case '6': cd[0] = WRITE_DATA + (dparm.mt_mfm_sk & 0xC0); read_write: printf(" Enter sector number: "); getnum("%d", §or); nbyte = 128 << dparm.nrec; cd[1] = hdsds; cd[2] = track; cd[3] = head; cd[4] = sector; cd[5] = dparm.nrec; cd[6] = dparm.eot; cd[7] = dparm.gpl; cd[8] = dparm.dtl; flop_cmd(unit, 1, 200, cd, rs, nbyte, buffer); if ((cd[0] & 0x0f) == READ_DATA) { puts(""); dump_buf(nbyte, buffer); } break; case '7': for (i = 0; i < nsector; i++) buffer[i * 4 + 2] = i; cd[0] = FORMAT_TRACK + (dparm.mt_mfm_sk & 0x40); cd[1] = hdsds; cd[2] = dparm.nrec; cd[3] = nsector; cd[4] = dparm.fgpl; /* gap length for format */ cd[5] = dparm.fd; /* fill byte for format */ fdc_cmd(unit, 1, 200, cd, rs, nsector * 4, buffer); break; case '8': printf("Enter number of times to read ids: "); getnum("%d", &j); drive_sel(unit, 1, 200); cd[0] = READ_ID + (dparm.mt_mfm_sk & 0x40); cd[1] = hdsds; for (i = 0; i < j; i++) { fdc_cmd(cd, rs, 0, buffer); buffer[i * 4 + 0] = rs[3]; buffer[i * 4 + 1] = rs[4]; buffer[i * 4 + 2] = rs[5]; buffer[i * 4 + 3] = rs[6]; } results(cd[0], rs); /* of the last command */ for (i = 0; i < j; i++) printf("c = %d, h = %d, r = %d, n = %d\n", buffer[i * 4 + 0], buffer[i * 4 + 1], buffer[i * 4 + 2], buffer[i * 4 + 3]); puts("Enter a key when ready"); getch(); puts(""); break; case 'R': dparm_rpt_chg(); break; case 'X': exit(0); break; default: printf("\nIllegal command.\n"); } } } char get_menu_choice() { char inp[80]; puts(""); puts("Menu:"); puts(" 0 - Drive #"); puts(" 1 - Specify (Step rate, Head unload, etc.)"); puts(" 2 - Recalibrate"); puts(" 3 - Seek (cylinder (track) number"); puts(" 4 - Head (surface)"); puts(" 5 - Read sector"); puts(" 6 - Write sector"); puts(" 7 - Format a track"); puts(" 8 - Read IDs"); puts(" R - Report and change the DPARM table"); puts(" X - Exit program"); puts("\n"); printf("Command: "); gets(inp); puts(""); return (isalpha(inp[0]) ? toupper(inp[0]) : inp[0]); } int dparm_rpt_chg() { int i, pi; unsigned int nv; UBYTE *a = &dparm.spec1; for (i = 0; i < NDPARM; i++) printf("(0x%02x) ", a[i]), puts(dparmmsg[i]); puts(""); printf("Which to change (enter -1 if none to change): "); getnum("%d", &pi); if ((pi < 0) || (pi >= NDPARM)) return; printf("dparm[%d] = %02x, new value: ", pi, a[pi]); getnum("%x", &nv); a[pi] = nv; } int fdc_cmd(UBYTE cmds[], UBYTE results[], int nbyte, UBYTE buffer[]) { /* fdc_cmd */ int i, ccode, s; ccode = cmds[0] & 0xF; if (cmdt[ccode].ctype == 'R') set_dma_up(buffer, 0, nbyte); /* set up DMA to read */ else if (cmdt[ccode].ctype == 'W') set_dma_up(buffer, 1, nbyte); /* set up DMA to write */ set_for_int(); for (i = 0; i < cmdt[ccode].ncmd; i++) { s = out_fdccmd(cmds[i]); if (s) printf("fdc_cmd: Error from out_fdccmd = %d\n", s); } if (cmdt[ccode].ctype == 'I') { if (s = wait_for_int()) printf("fdc_cmd: Error from wait_for_int = %d\n", s); s = out_fdccmd(SENSE_INTR_STATUS); if (s) printf("fdc_cmd: Error " "during sense_intr_stat from out_fdccmd = %d\n", s); in_fdcres(results++); /* from sense_intr_status */ in_fdcres(results); } else { if (cmdt[ccode].ctype != 'N') { if (s = wait_for_int()) printf("fdc_cmd: Error from wait_for_int = %d\n", s); } for (i = 0; i < cmdt[ccode].nres; i++) { s = in_fdcres(results++); if (s) printf("fdc_cmd: Error from in_fdcres = %d\n", s); } } } /* fdc_cmd */ int results(UBYTE cmd, UBYTE res[]) { int i; printf("\nresults :"); for (i=0; i < (cmdt[cmd & 0xf].nres); i++) printf("%02x ", res[i]); printf("\n"); } int dump_buf(int n, UBYTE buf[]) { /* dump_buf */ int i, j; for (i = 0; i < n; i++) { if ((i & 0xF) == 0) printf("%4x: ", i); else if ((i & 0x7) == 0) printf(" |"); printf(" %02x", buf[i]); if ((i & 0xF) == 15) { printf(" |"); for (j = i - 15; j <= i; j++) if (isprint(buf[j])) putchar(buf[j]); else putchar('.'); printf("|\n"); if ((i & 0xFF) == 0xFF) { printf("hit any key to continue"); getch(); puts(""); } } } } int set_motor_tmo(int count) { pokebyte(0x40, 0x40, &count); /* set motor_count for BIOS */ } int getnum(char *fmt, int *num) { char s[80]; gets(s); sscanf(s, fmt, num); } int out_fdccmd(UBYTE byte) { long st; st = start_tmo(); while ((in_stat() & 0xC0) != 0x80) if (check_tmo(st, 18)) return 1; out_dat(byte); return 0; } int in_fdcres(UBYTE *byte) { long st; st = start_tmo(); while ((in_stat() & 0xC0) != 0xC0) { if (check_tmo(st, 18)) return 2; } *byte = in_dat(); return 0; } int set_for_int() { /* set_for_int */ UBYTE seek_status; seek_status = 0; pokebyte(0x40, 0x3E, seek_status); } /* set_for_int */ int wait_for_int() { /* wait_for_int */ UBYTE seek_status; long t1; t1 = start_tmo(); do { if (check_tmo(t1, 18)) return 3; seek_status = peekbyte(0x40, 0x3E); } while (seek_status == 0); return 0; } /* wait_for_int */ #define DMACH2AD 0x04 #define DMACH2WC 0x05 #define DMACLRFF 0x0C #define DMAMODE 0x0B #define DMAWAMRB 0x0A /* WRITE A MASK REGISTER BIT */ #define PAGEREG 0x81 /* cmd = 0 for read, 1 for write */ int set_dma_up(char *ptr, int cmd, int count) { /* set_dma_up */ unsigned long paddr, test; UBYTE lobyte, hibyte, hinybl; struct SREGS seg; segread(&seg); paddr = (seg.ds * 0x10L) + (unsigned) ptr; lobyte = (char) (paddr & 0xFF); hibyte = (char) ((paddr >> 8) & 0xFF); hinybl = (char) ((paddr >> 16) & 0xF); test = (paddr & 0xFFFFL) + (unsigned) count; if (test & 0xFFFF0000L) { printf("paddr - attempt for dma to cross 64K boundary\n"); printf("paddr = 0x%08lx: %01x %02x %02x\n", paddr, hinybl, hibyte, lobyte); exit(0); } _disable(); /* for Turbo-C: disable(); */ outp(DMACLRFF, 0); /* clear byte ptr flip/flop */ if (cmd == 0) outp(DMAMODE, 0x46); else outp(DMAMODE, 0x4A); outp(DMACH2AD, lobyte); outp(DMACH2AD, hibyte); outp(PAGEREG, hinybl); count--; /* required for dma chip */ lobyte = (char) (count & 0xFF); hibyte = (char) ((count >> 8) & 0xFF); outp(DMACH2WC, lobyte); outp(DMACH2WC, hibyte); _enable(); /* for Turbo-C: enable(); */ outp(DMAWAMRB, 2); /* clear bit for ch 2, enabling transfer */ } /* set_dma_up */ int drive_sel(int unit, int motor, int timeout) { if (motor) set_motor_tmo(timeout); out_dor(0xC+unit+(motor?(0x10< startt + tmoticks) return 1; else return 0; } #pragma check_pointer(off) /* required here to let these work */ pokebyte(unsigned seg, unsigned off, UBYTE dbyte) { UBYTE far *sp; FP_SEG(sp) = seg; FP_OFF(sp) = off; /* for Turbo-C: sp = MK_FP(seg, off); */ *sp = dbyte; } UBYTE peekbyte(unsigned seg, unsigned off) { UBYTE far *sp; FP_SEG(sp) = seg; FP_OFF(sp) = off; /* for Turbo-C: sp = MK_FP(seg, off); */ return *sp; }